/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package gcb;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.util.Enumeration;
import java.util.Random;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.crypto.encodings.PKCS1Encoding;
import org.bouncycastle.crypto.engines.RSAEngine;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
/**
*
* @author wizardus
*/
public class GarenaEncrypt {
SecretKey skey;
SecretKeySpec skey_spec;
byte[] iv;
IvParameterSpec iv_spec;
PEMKeyPair rsaKey;
Random random;
public GarenaEncrypt() {
random = new Random();
}
public void initAES() {
Main.println(3, "[GEncrypt] Initializing AES Keys...");
KeyGenerator kgen = null;
try {
kgen = KeyGenerator.getInstance("AES");
} catch(NoSuchAlgorithmException nsae) {
if(Main.DEBUG){
nsae.printStackTrace();
}
Main.println(1, "[GEncrypt] Failed to initialize AES keys: " + nsae.getLocalizedMessage());
Main.println(1, "[GEncrypt] ... this probably means that your policy files are not updated");
Main.println(1, "[GEncrypt] ... see INSTALL for details on policy file configuration");
Main.println(1, "[GEncrypt] ... enable gcb_debug for full exception output");
}
kgen.init(256);
// Generate the secret key specs.
skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
skey_spec = new SecretKeySpec(raw, "AES");
iv = new byte[16];
random.nextBytes(iv);
iv_spec = new IvParameterSpec(iv);
}
public void initAES(byte[] raw, byte[] iv) {
this.iv = iv;
skey_spec = new SecretKeySpec(raw, "AES");
iv_spec = new IvParameterSpec(iv);
}
public void initRSA() {
Main.println(3, "[GEncrypt] Initializing RSA Keys...");
BouncyCastleProvider bcp = new BouncyCastleProvider();
Security.addProvider(bcp);
Main.println(3, "[GEncrypt] Reading private key in PEM format...");
try {
PEMParser pemreader = new PEMParser(new FileReader("gkey.pem"));
rsaKey = (PEMKeyPair) pemreader.readObject();
pemreader.close();
} catch(IOException ioe) {
if(Main.DEBUG) {
ioe.printStackTrace();
}
Main.println(1, "[GEncrypt] Failed to read the Garena RSA key: " + ioe.getLocalizedMessage());
Main.println(1, "[GEncrypt] ... check to make sure that gkey.pem exists in the working directory and is readable by gcb");
Main.println(1, "[GEncrypt] ... enable gcb_debug for full exception output");
}
}
public byte[] rsaEncryptPrivate(byte[] data) throws Exception {
AsymmetricKeyParameter privKey = PrivateKeyFactory.createKey(rsaKey.getPrivateKeyInfo());
RSAEngine engine = new RSAEngine();
PKCS1Encoding cipher = new PKCS1Encoding(engine);
cipher.init(true, privKey);
return cipher.processBlock(data, 0, data.length);
}
public byte[] rsaDecryptPrivate(byte[] data) throws Exception {
AsymmetricKeyParameter privKey = PrivateKeyFactory.createKey(rsaKey.getPrivateKeyInfo());
RSAEngine engine = new RSAEngine();
PKCS1Encoding cipher = new PKCS1Encoding(engine);
cipher.init(true, privKey);
return cipher.processBlock(data, 0, data.length);
}
public byte[] aesDecrypt(byte[] data) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skey_spec, iv_spec);
return cipher.doFinal(data);
}
public byte[] aesEncrypt(byte[] data) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skey_spec, iv_spec);
return cipher.doFinal(data);
}
public String md5(String in) {
byte[] data = in.getBytes();
try {
MessageDigest md = MessageDigest.getInstance("MD5");
return hexEncode(md.digest(data));
} catch(NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
//code from http://www.exampledepot.com/egs/java.util.zip/CompArray.html
public static byte[] deflate(byte[] input) {
// Create the compressor
Deflater compressor = new Deflater();
// Give the compressor the data to compress
compressor.setInput(input);
compressor.finish();
// Create an expandable byte array to hold the compressed data.
// You cannot use an array that's the same size as the orginal because
// there is no guarantee that the compressed data will be smaller than
// the uncompressed data.
ByteArrayOutputStream bos = new ByteArrayOutputStream(input.length);
// Compress the data
byte[] buf = new byte[1024];
while (!compressor.finished()) {
int count = compressor.deflate(buf);
bos.write(buf, 0, count);
}
try {
bos.close();
} catch (IOException e) {
}
// Get the compressed data
return bos.toByteArray();
}
public static byte[] expand(byte[] input) {
// Create the decompressor and give it the data to compress
Inflater decompressor = new Inflater();
decompressor.setInput(input);
// Create an expandable byte array to hold the decompressed data
ByteArrayOutputStream bos = new ByteArrayOutputStream(input.length);
// Decompress the data
byte[] buf = new byte[1024];
while (!decompressor.finished()) {
try {
int count = decompressor.inflate(buf);
bos.write(buf, 0, count);
} catch (DataFormatException e) {
}
}
try {
bos.close();
} catch (IOException e) {
}
// Get the decompressed data
return bos.toByteArray();
}
public static String strFromBytes(byte[] input) {
return strFromBytes(input, "UTF-8");
}
public static String strFromBytes(byte[] input, String charset) {
//find null byte
int null_index = input.length;
for(int i = 0; i < input.length; i++) {
if(input[i] == 0) {
null_index = i;
break;
}
}
try {
return new String(input, 0, null_index, charset);
} catch(UnsupportedEncodingException e) {
Main.println(1, "[GEncrypt] " + charset + " is unsupported: " + e.getLocalizedMessage());
return null;
}
}
public static String strFromBytes16(byte[] input) throws IOException {
//find null byte
int null_index = input.length;
for(int i = 0; i < input.length - 1; i+=2) {
if(input[i] == 0 && input[i + 1] == 0) {
null_index = i;
break;
}
}
try {
return new String(input, 0, null_index, "UnicodeLittleUnmarked");
} catch(UnsupportedEncodingException e) {
Main.println(1, "[GEncrypt] UnicodeLittleUnmarked is unsupported: " + e.getLocalizedMessage());
return null;
}
}
public static String cleanString(String string) {
byte[] input = string.getBytes();
for(int i = 0; i < input.length; i++) {
if(input[i] < 32 || input[i] > 126) {
//bad character, probably for formatting that we don't want
input[i] = 46; //period
}
}
return new String(input);
}
/**
* Convert the byte array to an int.
*
* @param b The byte array
* @return The integer
*/
public static int byteArrayToInt(byte[] b) {
return byteArrayToInt(b, 0);
}
/**
* Convert the byte array to an int starting from the given offset.
*
* @param b The byte array
* @param offset The array offset
* @return The integer
*/
public static int byteArrayToInt(byte[] b, int offset) {
int value = 0;
for (int i = 0; i < 4; i++) {
int shift = (4 - 1 - i) * 8;
value += (b[i + offset] & 0x000000FF) << shift;
}
return value;
}
public static int byteArrayToIntLength(byte[] b, int offset, int length) {
int value = 0;
for (int i = 0; i < length; i++) {
int shift = (length - 1 - i) * 8;
value += (b[i + offset] & 0x000000FF) << shift;
}
return value;
}
public static int byteArrayToIntLittle(byte[] b, int offset) {
int value = 0;
for (int i = 3; i >= 0; i--) {
int shift = i * 8;
value += (b[i + offset] & 0x000000FF) << shift;
}
return value;
}
public static int byteArrayToIntLittleLength(byte[] b, int offset, int length) {
int value = 0;
for (int i = 0; i < length; i++) {
int shift = i * 8;
value += (b[i + offset] & 0x000000FF) << shift;
}
return value;
}
public static short readShort(byte[] data, int offset) {
return (short) (((data[offset] << 8)) | ((data[offset + 1] & 0xff)));
}
public static byte[] shortToByteArray(short s) {
return new byte[] { (byte) ((s & 0xFF00) >> 8), (byte) (s & 0x00FF) };
}
public static int unsignedShort(short s) {
byte[] array = shortToByteArray(s);
int b1 = (0x000000FF & ((int) array[0]));
int b2 = (0x000000FF & ((int) array[1]));
return (b1 << 8 | b2);
}
public static int unsignedByte(byte b) {
return (0x000000FF & ((int)b));
}
public static String hexEncode(byte[] input) {
if (input == null || input.length == 0)
{
return "";
}
int inputLength = input.length;
StringBuilder output = new StringBuilder(inputLength * 2);
for (int i = 0; i < inputLength; i++)
{
int next = input[i] & 0xff;
if (next < 0x10)
{
output.append("0");
}
output.append(Integer.toHexString(next));
}
return output.toString();
}
public static byte[] hexEncode2(String first) {
byte[] ret = new byte[32];
//do some random stuff
for(int i = 0; i < first.length() && i < 32; i++) {
if(Character.isDigit(first.charAt(i))) {
ret[i] = (byte) (first.charAt(i) - 18);
} else {
ret[i] = (byte) (first.charAt(i) - 36);
}
}
return ret;
}
public static byte[] externalAddress() {
try {
URL whatismyip = new URL("http://checkip.dyndns.com/");
BufferedReader in = new BufferedReader(new InputStreamReader(whatismyip.openStream()));
String[] parts = in.readLine().split("<");
InetAddress ip = InetAddress.getByName(parts[6].substring(25));
Main.println(4, "[GEncrypt] External IP address determined at " + ip.getHostAddress());
return ip.getAddress();
} catch(IOException ioe) {
ioe.printStackTrace();
return null;
}
}
public static byte[] internalAddress() {
try {
InetAddress local_address = getFirstNonLoopbackAddress(true, false);
Main.println(4, "[GEncrypt] Internal IP address determined at " + local_address.getHostAddress());
return local_address.getAddress();
} catch(IOException ioe) {
ioe.printStackTrace();
return null;
}
}
private static InetAddress getFirstNonLoopbackAddress(boolean preferIpv4, boolean preferIPv6) throws SocketException {
Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
while (en.hasMoreElements()) {
NetworkInterface i = en.nextElement();
for (Enumeration<InetAddress> en2 = i.getInetAddresses(); en2.hasMoreElements();) {
InetAddress addr = en2.nextElement();
if (!addr.isLoopbackAddress()) {
if (addr instanceof Inet4Address) {
if (preferIPv6) {
continue;
}
return addr;
}
if (addr instanceof Inet6Address) {
if (preferIpv4) {
continue;
}
return addr;
}
}
}
}
return null;
}
//expensive but most correct integer checking
public static boolean isInteger(String string) {
try {
Integer.valueOf(string);
return true;
} catch (NumberFormatException e) {
return false;
}
}
public static String getTerminatedString(ByteBuffer buf) {
return new String(getTerminatedArray(buf));
}
public static byte[] getTerminatedArray(ByteBuffer buf) {
int start = buf.position();
while(buf.get() != 0) {}
int end = buf.position();
byte[] bytes = new byte[end - start - 1]; //don't include terminator
buf.position(start);
buf.get(bytes);
//put position after array
buf.position(end); //skip terminator
return bytes;
}
}